package controller

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"gitee.com/worklz/grpc-mtls-demo/gateway/app/dto"
	"gitee.com/worklz/grpc-mtls-demo/gateway/pkg/response"
	userconfig "gitee.com/worklz/grpc-mtls-demo/server-user/config"
	userpb "gitee.com/worklz/grpc-mtls-demo/server-user/proto/user"
	"github.com/gin-gonic/gin"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"log"
	"os"
	"path/filepath"
	"runtime"
)

type UserController struct {
	Controller
}

func (c *UserController) Detail(ctx *gin.Context) {
	// 数据获取校验
	req := dto.UserReq{}
	if err := ctx.ShouldBindJSON(&req); err != nil {
		response.Fail(ctx, err.Error())
		return
	}
	// 调用 grpc 用户服务获取用户数据
	client, conn, err := GetGrpcUserServiceClient()
	defer func() {
		if conn == nil {
			return
		}
		if closeErr := conn.Close(); closeErr != nil {
			log.Fatalf("关闭 grpc 用户服务连接失败！%v", closeErr)
		}
	}()
	if err != nil {
		response.Fail(ctx, err.Error())
	}
	userReq := &userpb.GetUserRequest{
		UserId: req.Id,
	}
	userRes, userErr := client.GetUser(context.Background(), userReq)
	if userErr != nil {
		response.Fail(ctx, fmt.Sprintf("获取用户数据失败: %v", userErr))
		return
	}
	response.Success(ctx, nil, userRes.Message)
}

// GetGrpcUserServiceClient 获取 grpc 用户服务客户端
func GetGrpcUserServiceClient() (client userpb.UserServiceClient, conn *grpc.ClientConn, err error) {
	// 调用封装的TLS配置方法（传入证书路径）
	creds, err := loadClientMTLSCredentials(userconfig.ServerName)
	if err != nil {
		err = fmt.Errorf("加载网关客户端TLS凭证失败: %v", err)
		return
	}

	conn, err = grpc.NewClient(
		userconfig.ServerAddr,
		grpc.WithTransportCredentials(creds),
		// 可添加其他选项，如超时设置
		// grpc.WithTimeout(5*time.Second),
	)
	if err != nil {
		return
	}
	client = userpb.NewUserServiceClient(conn)
	return
}

// loadClientMTLSCredentials 加载访问其他服务客户端的mTLS凭证（需要提交给服务端验证）
// serverName 要访问的服务名称
func loadClientMTLSCredentials(serverName string) (credentials.TransportCredentials, error) {
	// 获取项目根目录绝对路径
	_, filename, _, _ := runtime.Caller(0)
	projectRoot := filepath.Dir(filepath.Dir(filename)) // 根据实际项目结构调整层级
	demoRootPath := filepath.Join(projectRoot, "..", "..")

	// 拼接CA根证书路径
	caPath := filepath.Join(demoRootPath, "cert", "ca.pem")
	// 加载CA根证书
	caCert, err := os.ReadFile(caPath)
	if err != nil {
		return nil, err
	}
	caPool := x509.NewCertPool()
	if !caPool.AppendCertsFromPEM(caCert) {
		return nil, err
	}

	// 拼接网关客户端证书和私钥路径
	certPath := filepath.Join(demoRootPath, "gateway", "cert", "gateway.pem")
	keyPath := filepath.Join(demoRootPath, "gateway", "cert", "gateway-key.pem")
	// 加载客户端证书和私钥
	clientCert, err := tls.LoadX509KeyPair(certPath, keyPath)
	if err != nil {
		return nil, err
	}

	// 配置TLS（双向认证）
	tlsConfig := &tls.Config{
		Certificates: []tls.Certificate{clientCert}, // 提供当前客户端证书（用于服务端验证）
		RootCAs:      caPool,                        // 验证要访问服务的CA根证书
		ServerName:   serverName,                    // 要访问服务的证书CN（用于验证）
	}

	return credentials.NewTLS(tlsConfig), nil
}
